package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/inventory"
	"bgs/grpc/imp/enums"
	orderRunner "bgs/nsqp/runners/order"
	"bgs/service/model"
	"bgs/util"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// GenerateOrderNumber 单号生成器
func (s *OrderServiceImp) GenerateOrderNumber(activityTypeInt int64, userID int64) string {
	// O+保留(3)+活动类型(3)+用户id(10)+(秒时间戳12)+随机数(3) = 32
	return fmt.Sprintf("O%03d%s%s%s%03d",
		0,
		util.PaddingLeftZero(activityTypeInt, 3),
		util.PaddingLeftZero(userID, 10),
		util.GoTimeToStringf(time.Now(), "060102150405"),
		util.RandomInt(0, 999))
}

// CreateOrder 创建购买订单
func (s *OrderServiceImp) CreateOrder(ctx context.Context, q *sqlc.Queries, createOrderParams sqlc.CreateOrderParams, upsertOrderDetailParams []sqlc.UpsertOrderDetailParams, orderExtInfoPtr *model.OrderExtInfo) (order sqlc.Order, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	order, err = q.CreateOrder(ctx, createOrderParams)
	if err != nil {
		return
	}
	slog.Infof("1 创建订单 ok")

	mapOrderIDWithProductSpecKeyToOrderDetailID := make(map[string]int64)
	var orderDetail sqlc.OrderDetail
	for _, item := range upsertOrderDetailParams {
		item.OrderID = order.ID
		item.OrderSerialNumber = order.SerialNumber
		orderDetail, err = q.UpsertOrderDetail(ctx, item)
		if err != nil {
			return
		}
		mapOrderIDWithProductSpecKeyToOrderDetailID[orderDetail.ProductSpecKey] = orderDetail.ID
	}
	slog.Infof("2 创建订单明细 ok")

	// 需要将orderDetailID加入到ExtInfo
	for _, item := range orderExtInfoPtr.Details {
		item.OrderDetailID = mapOrderIDWithProductSpecKeyToOrderDetailID[item.ProductSpecKey]
	}
	extInfo, marshalErr := json.Marshal(orderExtInfoPtr)
	if marshalErr != nil {
		err = marshalErr
		return
	}

	order, err = q.UpdateOrderExtInfo(ctx, sqlc.UpdateOrderExtInfoParams{
		ID:      order.ID,
		ExtInfo: extInfo,
	})
	slog.Infof("3 更新orderExtInfo ok")

	return
}

// CreateInventoryFreezeOrderForC 创建仓库侧冻结单(for c)
func (s *OrderServiceImp) CreateInventoryFreezeOrderForC(ctx context.Context, orderPtr *sqlc.Order, normalOrderDetailWithProductAndSpecInfos []sqlc.ListNormalOrderDetailWithProductAndSpecInfosByOrderSerialNumberRow, warehouseID int64) (inventoryFreezeOrderNumber string, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	// 拿取sg_id、活动仓库ckid,活动库存的storageKey
	brandID := orderPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID

	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse :%d", warehouseID)
		return
	}
	ckID := cWarehouse.CkID

	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(orderPtr.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal ext_info失败:%s", err.Error())
		return
	}

	var details []*inventory.CreateFreezeOrderDetail
	for _, item := range orderExtInfo.Details {
		details = append(details, &inventory.CreateFreezeOrderDetail{
			Id:                ckID, //远端仓库id
			StorageType:       inventory.StorageType_PROD,
			StorageKey:        item.StorageKey,
			Quantity:          int64(item.Quantity),
			StorageName:       item.ProductWithSpecName,               // product_name+spec_name
			FreezeOrderNumber: orderExtInfo.ActivityInventoryFreezeNo, // 活动对应的冻结单号
			WeightRef:         0,
		})
	}

	req := &inventory.CreateFreezeOrderRequest{
		SgId:       sgID,
		RequestNum: orderPtr.SerialNumber,
		Source:     inventory.SourceEnum_XT_ORDER,
		MemberId:   strconv.FormatInt(orderPtr.UserID, 10),
		ExpireTime: "", // 空字符代表永不过期
		Details:    details,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()

	reply, err := s.proxy.InventoryCl.CreateFreezeOrder(ctxWithTimout, req)
	if err != nil {
		errReply := s.proxy.GetInventoryErrorInfo(err)
		if errReply != nil {
			err = fmt.Errorf("仓库侧创建冻结单错误:%s", errReply.ErrorMessage)
			slog.Infof("InventoryCl.CreateFreezeOrder of user err====>type:%s,code:%s,msg:%s", errReply.ErrorType, errReply.ErrorCode, errReply.ErrorMessage)
		}
		return
	}

	inventoryFreezeOrderNumber = reply.OrderNumber
	slog.Infof("1 创建仓库侧冻结单 ok")
	return
}

// CancelOrderParam cancel order param
type CancelOrderParam struct {
	OrderID      int64
	CancelReason string
}

// CancelOrder cancel order (主动)
func (s *OrderServiceImp) CancelOrder(ctx context.Context, q *sqlc.Queries, p CancelOrderParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	_, err = q.CancelOrder(ctx, sqlc.CancelOrderParams{
		ID:           p.OrderID,
		CancelReason: sqlc.NSFromString(p.CancelReason),
	})
	if err != nil {
		return err
	}
	slog.Infof("1 更新订单状态=>before_pay_closed ok")

	return
}

// CloseOrderForPayingExpired cancel order (主动)
func (s *OrderServiceImp) CloseOrderForPayingExpired(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	_, err = q.FlowOrderFromPendingToClosed(ctx, orderID)
	if err != nil {
		return err
	}
	slog.Infof("1 更新订单状态=>before_pay_closed ok")
	return
}

// PrepareOrderParam 商家备货
type PrepareOrderParam struct {
	OrderPtr *sqlc.Order
	BrandID  int64
}

// PrepareOrder pick order (主动)
func (s *OrderServiceImp) PrepareOrder(ctx context.Context, q *sqlc.Queries, p PrepareOrderParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(p.OrderPtr.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal ext_info失败:%s", err.Error())
		return
	}

	brandID := p.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID
	warehouseID := orderExtInfo.WarehouseID
	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse :%d", warehouseID)
		return
	}
	ckID := cWarehouse.CkID

	// 获取仓库发货单
	enforcement, err := q.GetEnforcementByOrderSerialNumber(ctx, p.OrderPtr.SerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1 获取履约单 ok")

	req := &inventory.CommonOrderRequest{
		WebToken: &inventory.WebToken{
			EmpId: uint32(cBrand.Ext.OwnUserID),
			SgId:  uint32(sgID),
		},
		PStoreId:    0,
		Id:          uint32(ckID),
		OrderNumber: enforcement.DeliveryNumber.String,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()

	_, err = s.proxy.InventoryCl.TagOrderReady(ctxWithTimout, req)
	if err != nil {
		errReply := s.proxy.GetInventoryErrorInfo(err)
		if errReply != nil {
			err = fmt.Errorf("仓库侧备货错误:%s", errReply.ErrorMessage)
			slog.Infof("InventoryCl.TagOrderReady err====>type:%s,code:%s,msg:%s", errReply.ErrorType, errReply.ErrorCode, errReply.ErrorMessage)
		}
		return
	}

	slog.Infof("2 提交仓库侧备货 ok")
	return
}

// DeliverOrderParam 商家发货
type DeliverOrderParam struct {
	OrderPtr            *sqlc.Order
	PlatformID          uint32
	PlatformOrderNumber string
	BrandID             int64
}

// DeliverOrder deliver order (主动)
func (s *OrderServiceImp) DeliverOrder(ctx context.Context, q *sqlc.Queries, p DeliverOrderParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(p.OrderPtr.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal ext_info失败:%s", err.Error())
		return
	}

	brandID := p.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID
	warehouseID := orderExtInfo.WarehouseID
	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse :%d", warehouseID)
		return
	}
	ckID := cWarehouse.CkID

	// 获取仓库发货单
	enforcement, err := q.GetEnforcementByOrderSerialNumber(ctx, p.OrderPtr.SerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1 获取履约单 ok")

	req := &inventory.DeliveryProcessOrderRequest{
		WebToken: &inventory.WebToken{
			EmpId: uint32(cBrand.Ext.OwnUserID),
			SgId:  uint32(sgID),
		},
		PStoreId:            0,
		Id:                  uint32(ckID),
		OrderNumber:         enforcement.DeliveryNumber.String,
		PlatformId:          p.PlatformID,
		PlatformOrderNumber: p.PlatformOrderNumber,
		PlatformDeliveryFee: 0,
		DeliverType:         inventory.DeliverType_ORDINARY,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()

	_, err = s.proxy.InventoryCl.DeliveryOrder(ctxWithTimout, req)
	if err != nil {
		errReply := s.proxy.GetInventoryErrorInfo(err)
		if errReply != nil {
			err = fmt.Errorf("仓库侧发货错误:%s", errReply.ErrorMessage)
			slog.Infof("InventoryCl.DeliveryOrder err====>type:%s,code:%s,msg:%s", errReply.ErrorType, errReply.ErrorCode, errReply.ErrorMessage)
		}
		return
	}

	slog.Infof("1 提交仓库侧备货 ok")
	return
}

// PickOrderParam user pickup
type PickOrderParam struct {
	IsFromUser        bool
	OrderSerialNumber string
	Picked            time.Time
	PickupCode        string
}

// PickOrder pick order (主动,发送给远端消息)
func (s *OrderServiceImp) PickOrder(ctx context.Context, q *sqlc.Queries, p PickOrderParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	enforcment, err := q.GetEnforcementByOrderSerialNumber(ctx, p.OrderSerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1、获取本地履约单 ok")

	if !p.IsFromUser {
		if p.PickupCode != enforcment.PickupCode.String {
			return fmt.Errorf("自提码不匹配")
		}
	}

	_, err = q.FlowOrderFromPreparedToFinished(ctx, enforcment.OrderID)
	if err != nil {
		return
	}
	slog.Infof("3、订单流转到完成 ok")
	enforcment, err = q.UpdateOrderEnforcementForPickedByInventory(ctx, sqlc.UpdateOrderEnforcementForPickedByInventoryParams{
		ID:     enforcment.ID,
		Picked: sqlc.NTimeFromTime(p.Picked),
	})
	if err != nil {
		return
	}
	slog.Info("4、更新本地履约单信息 ok")

	// 发送订单手工自提
	err = s.orderRunner.PublishOrderPicked(orderRunner.PublishOrderPickedParam{
		OrderSerialNumber: p.OrderSerialNumber,
		Picked:            p.Picked,
	})
	if err != nil {
		return err
	}
	slog.Infof("5 发送确认自提订单消息 ok")

	return
}

// ReceiveOrderParam user receive param
type ReceiveOrderParam struct {
	OrderPtr *sqlc.Order
	Received time.Time
}

// ReceiveOrder receive order (主动,发送给远端消息)
func (s *OrderServiceImp) ReceiveOrder(ctx context.Context, q *sqlc.Queries, p ReceiveOrderParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	enforcment, err := q.GetEnforcementByOrderSerialNumber(ctx, p.OrderPtr.SerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1、获取本地履约单 ok")

	if p.OrderPtr.OrderStatus != string(enums.OrderStatusDelivered) {
		return fmt.Errorf("订单状态不匹配: %s,应为delivered", p.OrderPtr.OrderStatus)
	}

	_, err = q.FlowOrderFromDeliveredToFinished(ctx, enforcment.OrderID)
	if err != nil {
		return
	}
	slog.Infof("3、订单流转到已收货 ok")
	enforcment, err = q.UpdateOrderEnforcementForReceivedByInventory(ctx, sqlc.UpdateOrderEnforcementForReceivedByInventoryParams{
		ID:       enforcment.ID,
		Received: sqlc.NTimeFromTime(p.Received),
	})
	if err != nil {
		return
	}
	slog.Info("4、更新本地履约单信息 ok")

	// 发送确认收货消息
	err = s.orderRunner.PublishOrderReceived(orderRunner.PublishOrderReceivedParam{
		OrderSerialNumber: p.OrderPtr.SerialNumber,
		Received:          p.Received,
	})
	if err != nil {
		return err
	}
	slog.Infof("5 发送确认收货订单消息 ok")

	return
}

// NeedProfitReturn 订单是否需要分账
func (s *OrderServiceImp) NeedProfitShare(orderPtr *sqlc.Order) bool {
	return CheckNeedProfitShare(orderPtr)
}

// 场景-> 在订单正常完成以后到订单截止售后之间的时间段(obsolete)
// NeedProfitReturn 需要分账回退
func (s *OrderServiceImp) NeedProfitReturn(orderPtr *sqlc.Order) bool {
	return CheckNeedProfitShare(orderPtr) && orderPtr.OrderStatus == string(sqlc.EmOrderStatusFinished) && util.IsAfterNow(orderPtr.AfterSaleDeadlineAt.Time)
}

// Obsolete 原来只针对代言人订单奖励金
// func (s *OrderServiceImp) NeedProfitReturn(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order) bool {
// 	return orderPtr.NeedProfit && orderPtr.IsProfited && (orderPtr.BonusAmount+orderPtr.RefundBonusAmount) != 0
// }

// MarkOrderParam 标记订单进入售后状态参数
type MarkOrderParam struct {
	OrderPtr *sqlc.Order
}

// MarkOrderIntoApplyAfterSale 标记订单已申请售后 (主动)
func (s *OrderServiceImp) MarkOrderIntoApplyAfterSale(ctx context.Context, q *sqlc.Queries, p MarkOrderParam) (order sqlc.Order, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	order, err = q.UpdateOrderIsApplyForAfterSale(ctx, p.OrderPtr.ID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单进入售后状态 ok")

	return
}

// MarkOrderIntoAfterSale 标记订单进入售后状态 (主动)
func (s *OrderServiceImp) MarkOrderIntoAfterSale(ctx context.Context, q *sqlc.Queries, p MarkOrderParam) (order sqlc.Order, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	order, err = q.UpdateOrderIsInAfterSale(ctx, p.OrderPtr.ID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单进入售后状态 ok")

	return
}

// MarkOrderProfited 标记订单已分账 (主动)
func (s *OrderServiceImp) MarkOrderProfited(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.MarkOrderProfited(ctx, orderID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单已分账 ok")

	return
}

// MarkOrderAccounted 标记订单已记账 (主动)
func (s *OrderServiceImp) MarkOrderAccounted(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.MarkOrderAccounted(ctx, orderID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单已记账 ok")

	return
}

// MarkOrderCounted 标记订单已统计 (主动)
func (s *OrderServiceImp) MarkOrderCounted(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.MarkOrderCounted(ctx, orderID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单已统计 ok")

	return
}

// MarkOrderCounted 标记订单已记账和已统计 (主动)
func (s *OrderServiceImp) MarkOrderAccountedAndCounted(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.MarkOrderAccountedAndCounted(ctx, orderID)
	if err != nil {
		return
	}
	slog.Info("1、标记订单已记账和已统计 ok")

	return
}

// MarkOrderCounted 标记订单已结算 (主动)
func (s *OrderServiceImp) MarkOrderSettled(ctx context.Context, q *sqlc.Queries, orderID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.MarkOrderSettled(ctx, orderID)
	if err != nil {
		return
	}
	slog.Info("标记订单已结算 ok")

	return
}

// DoAfterSaleDoneParam 售后完结处理订单的参数
type DoAfterSaleDoneParam struct {
	AfterSalePtr *sqlc.AfterSale
}

// DoAfterSaleDone 售后完结处理订单(被动)
func (s *OrderServiceImp) DoAfterSaleDone(ctx context.Context, q *sqlc.Queries, p DoAfterSaleDoneParam) (order sqlc.Order, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	// 可以锁订单
	order, err = q.GetOrder(ctx, p.AfterSalePtr.OrderID)
	if err != nil {
		return
	}
	slog.Infof("1 读取订单 ok")

	refundAmount := order.RefundAmount - p.AfterSalePtr.Amount                                                // 负值
	refundPlatformCommssion := order.RefundPlatformCommissionAmount + p.AfterSalePtr.PlatformCommissionAmount // 正值
	refundDistributorCommssion := order.RefundDistributorCommission + p.AfterSalePtr.DistributorCommission    // 正值
	refundBonusAmount := order.RefundBonusAmount + p.AfterSalePtr.BonusAmount                                 // 正值
	refundWxpayCommissionAmount := order.RefundWxpayCommissionAmount + p.AfterSalePtr.WxpayCommissionAmount   // 正值
	orderStatus := order.OrderStatus

	if orderStatus == string(sqlc.EmOrderStatusPayed) && p.AfterSalePtr.IsWholeRefund {
		// 订单是已支付并且整单售后退款,售后成功要将状态改为 after_pay_closed
		orderStatus = string(sqlc.EmOrderStatusAfterPayClosed)
	} else if orderStatus == string(sqlc.EmOrderStatusDelivered) || orderStatus == string(sqlc.EmOrderStatusPrepared) {
		// 订单是处于已发货时,运费产生但不退云分, 处于已备货,没有运费,所以能退的总金额一定是ProductAmount
		if order.ProductAmount+refundAmount == 0 {
			// 退款金额=商品金额,订单标记为付款后关闭
			orderStatus = string(sqlc.EmOrderStatusAfterPayClosed)
		}
	}

	order, err = q.UpdateOrderWhenAfterSaleDone(ctx, sqlc.UpdateOrderWhenAfterSaleDoneParams{
		ID:                             p.AfterSalePtr.OrderID,
		RefundAmount:                   refundAmount,
		RefundDistributorCommission:    refundDistributorCommssion,
		RefundBonusAmount:              refundBonusAmount,
		RefundWxpayCommissionAmount:    refundWxpayCommissionAmount,
		RefundPlatformCommissionAmount: refundPlatformCommssion,
		OrderStatus:                    orderStatus,
	})
	if err != nil {
		return
	}
	slog.Infof("2 售后完结处理订单 ok")

	afterSaleDetails, err := q.ListNotRefusedAfterSaleDetailsByAfterSaleID(ctx, p.AfterSalePtr.ID)
	if err != nil {
		return
	}
	slog.Infof("2 获取售后明细 ok")

	for _, item := range afterSaleDetails {
		_, err = q.UpdateOrderDetailWhenAfterSaleDone(ctx, sqlc.UpdateOrderDetailWhenAfterSaleDoneParams{
			AfterSaleSerialNumber: sqlc.NSFromString(p.AfterSalePtr.SerialNumber),
			ProductSpecKey:        item.ProductSpecKey,
			AfterSaleQuantity:     sqlc.NInt32FromInt32(item.Quantity),
			RefundAmount:          sqlc.NInt64FromInt64(0 - item.Amount),
		})
		if err != nil {
			return
		}
	}
	slog.Infof("3 售后完结处理订单明细 ok")
	return
}

// DoAfterSaleRefuseOrCancelParam 售后单被商家拒绝或者用户自己取消后处理订单参数
type DoAfterSaleRefuseOrCancelParam struct {
	AfterSalePtr *sqlc.AfterSale
}

// DoAfterSaleRefuseOrCancel 售后单被商家拒绝或者用户自己取消后处理订单(主动)
func (s *OrderServiceImp) DoAfterSaleRefuseOrCancel(ctx context.Context, q *sqlc.Queries, p DoAfterSaleRefuseOrCancelParam) (order sqlc.Order, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	order, err = q.UpdateOrderWhenAfterSaleRefuseOrCancel(ctx, p.AfterSalePtr.OrderID)
	if err != nil {
		return
	}
	slog.Infof("1 售后单被商家拒绝或者用户自己取消后处理订单 ok")

	afterSaleDetails, err := q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, p.AfterSalePtr.ID)
	if err != nil {
		return
	}
	slog.Infof("2 获取售后明细 ok")

	for _, item := range afterSaleDetails {
		_, err = q.UpdateOrderDetailWhenAfterSaleRefuseOrCancel(ctx, sqlc.UpdateOrderDetailWhenAfterSaleRefuseOrCancelParams{
			AfterSaleSerialNumber: sqlc.NSFromString(p.AfterSalePtr.SerialNumber),
			ProductSpecKey:        item.ProductSpecKey,
		})
		if err != nil {
			return
		}
	}
	slog.Infof("3 售后单被商家拒绝或者用户自己取消后处理订单明细 ok")
	return
}
